home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / CPP / SETTIME.ZIP / CWINSOCK.CPP < prev    next >
C/C++ Source or Header  |  1995-12-19  |  52KB  |  1,717 lines

  1. // cwinsock.cpp : implementation file
  2. //
  3.  
  4. #include "afxwin.h"
  5. #include "afxext.h"
  6. #include <memory.h>
  7. #include <stdlib.h>
  8. #include "cwinsock.h"
  9.  
  10. #ifdef _DEBUG
  11. #undef THIS_FILE
  12. static char BASED_CODE THIS_FILE[] = __FILE__;
  13. #endif
  14.  
  15. /////////////////////////////////////////////////////////////////////////////
  16. // CWinSock
  17. /////////////////////////////////////////////////////////////////////////////
  18.  
  19. /////////////////////////////////////////////////////////////////////////////
  20. // CWinSock constructor
  21. //
  22. // Constructs the CWinSock object. Initializes member variables
  23. //
  24. CWinSock::CWinSock(WORD wVersionRequired/*= MAKEWORD(1, 1)*/)
  25. {
  26.   // initialize member variables
  27.   m_wVersionRequired = wVersionRequired;
  28.   m_nLastError = 0;
  29. }
  30.  
  31. /////////////////////////////////////////////////////////////////////////////
  32. // CWinSock::Startup()
  33. //
  34. // Start the WinSock sub-system.
  35. //
  36. int CWinSock::Startup()
  37. {
  38.   int nStatus = CWINSOCK_NOERROR;
  39.  
  40.   m_nLastError = WSAStartup(m_wVersionRequired, &m_wsaData);
  41.  
  42.   if (m_nLastError != 0)
  43.     nStatus = CWINSOCK_WINSOCK_ERROR;
  44.  
  45.   return nStatus;
  46. }
  47.  
  48. /////////////////////////////////////////////////////////////////////////////
  49. // CWinSock::Shutdown()
  50. //
  51. // Shutdown the WinSock sub-system.
  52. //
  53. int CWinSock::Shutdown()
  54. {
  55.   int nStatus = CWINSOCK_NOERROR;
  56.  
  57.   if (WSACleanup() != 0)
  58.   {
  59.     m_nLastError = WSAGetLastError();
  60.     nStatus = CWINSOCK_WINSOCK_ERROR;
  61.   }
  62.  
  63.   return nStatus;
  64. }
  65.  
  66. /////////////////////////////////////////////////////////////////////////////
  67. // CWinSock::Information()
  68. //
  69. // Copy the WinSock information structure.
  70. //
  71. void CWinSock::Information(LPWSADATA pwsaData)
  72. {
  73.   memcpy(pwsaData, &m_wsaData, sizeof(WSADATA));
  74. }
  75.  
  76. /////////////////////////////////////////////////////////////////////////////
  77. // CDatagramSocket
  78. /////////////////////////////////////////////////////////////////////////////
  79.  
  80. /////////////////////////////////////////////////////////////////////////////
  81. // CDatagramSocket constructor
  82. //
  83. // Constructs the CDatagramSocket object. Initializes member variables
  84. //
  85. CDatagramSocket::CDatagramSocket(CWnd *pParentWnd, UINT uMsg)
  86. {
  87.   // initialize member variables
  88.   m_pParentWnd = pParentWnd;
  89.   ASSERT(m_pParentWnd != NULL);
  90.   m_uMsg = uMsg;
  91.   ASSERT(m_uMsg != 0);
  92.   InitVars();
  93. }
  94.  
  95. /////////////////////////////////////////////////////////////////////////////
  96. // CDatagramSocket destructor
  97. //
  98. CDatagramSocket::~CDatagramSocket()
  99. {
  100. }
  101.  
  102. /////////////////////////////////////////////////////////////////////////////
  103. // CDatagramSocket::InitVars()
  104. //
  105. // Initialize class member variables.
  106. //
  107. void CDatagramSocket::InitVars(BOOL bInitLastError/*= TRUE*/)
  108. {
  109.   if (bInitLastError)
  110.     m_nLastError = 0;
  111.  
  112.   m_s = INVALID_SOCKET;
  113.   memset(&m_sinLocal, 0, sizeof(SOCKADDR_IN));
  114.   m_bServer = FALSE;
  115. }
  116.  
  117. /////////////////////////////////////////////////////////////////////////////
  118. // CDatagramSocket::CreateSocket()
  119. //
  120. // Create a hidden window that will receive asynchronous messages
  121. // from WinSock.  Also creates a socket and optionally binds it to
  122. // a name if the socket is a server socket.
  123. //
  124. // This version of the CreateSocket() function takes a
  125. // port number, in host order, as input.  A port number
  126. // should only be specified if the socket is to be bound
  127. // to a certain port.  If you don't care which port is
  128. // assigned to the socket, just call CreateSocket() without
  129. // any parameter, causing CreateSocket(NULL) to be called.
  130. //
  131. int CDatagramSocket::CreateSocket(int nLocalPort)
  132. {
  133.   // if this version of the function is being called,
  134.   // a valid port number must be specified
  135.   if (nLocalPort <= 0)
  136.     return CWINSOCK_PROGRAMMING_ERROR;
  137.  
  138.   // convert the port number into a string and
  139.   // call the version of CreateSocket() which
  140.   // accepts a string
  141.   char pszLocalService[18];
  142.   _itoa(nLocalPort, pszLocalService, 10);
  143.   return CreateSocket(pszLocalService);
  144. }
  145.  
  146. /////////////////////////////////////////////////////////////////////////////
  147. // CDatagramSocket::CreateSocket()
  148. //
  149. // Create a hidden window that will receive asynchronous messages
  150. // from WinSock.  Also creates a socket and optionally binds it to
  151. // a name if the socket is a server socket.
  152. //
  153. // This version of the CreateSocket() function takes a
  154. // string containing a service name or port number.
  155. // A parameter should only be specified if the socket is to be
  156. // bound to a certain port.  If you don't care which port is
  157. // assigned to the socket, just call CreateSocket() without
  158. // any parameter, causing CreateSocket(NULL) to be called.
  159. //
  160. int CDatagramSocket::CreateSocket(LPSTR pszLocalService/*= NULL*/)
  161. {
  162.   int nStatus = CWINSOCK_NOERROR;
  163.  
  164.   while (1)
  165.   {
  166.     // Make sure the socket isn't already created.
  167.     // If the socket handle is valid, return from this
  168.     // function right away so the existing parameters of
  169.     // the object are not tampered with.
  170.     if (m_s != INVALID_SOCKET)
  171.       return CWINSOCK_PROGRAMMING_ERROR;
  172.  
  173.     InitVars();
  174.  
  175.     // create the hidden window
  176.     RECT rect;
  177.     rect.left = 0;
  178.     rect.top = 0;
  179.     rect.right = 100;
  180.     rect.bottom = 100;
  181.     if (Create(NULL, NULL, WS_OVERLAPPEDWINDOW, rect, m_pParentWnd, 0) == 0)
  182.     {
  183.       nStatus = CWINSOCK_WINDOWS_ERROR;
  184.       break;
  185.     }
  186.  
  187.     // create the socket
  188.     m_s = socket(PF_INET, SOCK_DGRAM, 0);
  189.     if (m_s == INVALID_SOCKET)
  190.     {
  191.       m_nLastError = WSAGetLastError();
  192.       nStatus = CWINSOCK_WINSOCK_ERROR;
  193.       DestroyWindow();
  194.       break;
  195.     }
  196.  
  197.     // If pszLocalService is not NULL, this is a server socket
  198.     // that will accept data on the specified port.
  199.     if (pszLocalService != NULL)
  200.     {
  201.       // this socket is bound to a port number
  202.       // so set the server flag
  203.       m_bServer = TRUE;
  204.  
  205.       // assign the address family
  206.       m_sinLocal.sin_family = AF_INET;
  207.  
  208.       // assign the service port (may have to do a database lookup
  209.       // if a service port number was not specified)
  210.       m_sinLocal.sin_port = htons(atoi(pszLocalService));
  211.       if (m_sinLocal.sin_port == 0)
  212.       {
  213.         LPSERVENT pSent = getservbyname(pszLocalService, "udp");
  214.         if (pSent == NULL)
  215.         {
  216.           m_nLastError = WSAGetLastError();
  217.           nStatus = CWINSOCK_WINSOCK_ERROR;
  218.           closesocket(m_s);
  219.           DestroyWindow();
  220.           break;
  221.         }
  222.         m_sinLocal.sin_port = pSent->s_port;
  223.       }
  224.  
  225.       // assign the IP address
  226.       m_sinLocal.sin_addr.s_addr = htonl(INADDR_ANY);
  227.  
  228.       // bind the server socket to the name containing the port
  229.       if (bind(m_s, (LPSOCKADDR)&m_sinLocal, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
  230.       {
  231.         m_nLastError = WSAGetLastError();
  232.         nStatus = CWINSOCK_WINSOCK_ERROR;
  233.         closesocket(m_s);
  234.         DestroyWindow();
  235.         break;
  236.       }
  237.     }
  238.  
  239.     // start asynchronous event notification
  240.     long lEvent = FD_READ | FD_WRITE;
  241.     if (WSAAsyncSelect(m_s, m_hWnd, CWINSOCK_EVENT_NOTIFICATION, lEvent) ==
  242.      SOCKET_ERROR)
  243.     {
  244.       m_nLastError = WSAGetLastError();
  245.       nStatus = CWINSOCK_WINSOCK_ERROR;
  246.       closesocket(m_s);
  247.       DestroySocket();
  248.       break;
  249.     }
  250.  
  251.     break;
  252.   }
  253.  
  254.   // if anything failed in this function, set the
  255.   // socket variables appropriately
  256.   if (nStatus != CWINSOCK_NOERROR)
  257.     InitVars(FALSE);
  258.  
  259.   return nStatus;
  260. }
  261.  
  262. /////////////////////////////////////////////////////////////////////////////
  263. // CDatagramSocket::DestroySocket()
  264. //
  265. // Close the socket, remove any queued data,
  266. // and destroy the hidden window.
  267. //
  268. int CDatagramSocket::DestroySocket()
  269. {
  270.   int nStatus = CWINSOCK_NOERROR;
  271.  
  272.   // make sure the socket is valid
  273.   if (m_s == INVALID_SOCKET)
  274.     nStatus = CWINSOCK_PROGRAMMING_ERROR;
  275.   else
  276.   {
  277.     // remove any data in the write queue
  278.     while (!m_listWrite.IsEmpty())
  279.     {
  280.       LPDATAGRAMDATA pDatagramData = (LPDATAGRAMDATA)m_listWrite.RemoveHead();
  281.       LPVOID pData = pDatagramData->pData;
  282.       delete pDatagramData;
  283.  
  284.       m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
  285.        (LPARAM)pData);
  286.     }
  287.  
  288.     // remove any data in the read queue
  289.     while (!m_listRead.IsEmpty())
  290.     {
  291.       LPDATAGRAMDATA pDatagramData = (LPDATAGRAMDATA)m_listRead.RemoveHead();
  292.       free(pDatagramData->pData);
  293.       delete pDatagramData;
  294.     }
  295.  
  296.     // close the socket and initialize variables
  297.     closesocket(m_s);
  298.     InitVars();
  299.  
  300.     // destroy the hidden window
  301.     DestroyWindow();
  302.   }
  303.  
  304.   return nStatus;
  305. }
  306.  
  307. /////////////////////////////////////////////////////////////////////////////
  308. // CDatagramSocket::Write()
  309. //
  310. // Write data to the socket specified by the name and port.
  311. //
  312. // This version of the Write() function takes an integer
  313. // representing the length of the data to send, a pointer
  314. // to the data to send, a pointer to a string representing
  315. // the host name to send the data to, and an integer
  316. // representing the port number to send to.
  317. //
  318. // The data pointed to by pData must remain valid until either
  319. // the Write() function returns with an error, or the
  320. // write's completion is notified by the m_uMsg being sent
  321. // to the window that owns this datagram object with wParam set
  322. // to CWINSOCK_DONE_WRITING or CWINSOCK_ERROR_WRITING.
  323. //
  324. int CDatagramSocket::Write(int nLen, LPVOID pData,
  325.  LPSTR pszRemoteName, int nRemotePort)
  326. {
  327.   // convert the port number into a string and
  328.   // call the version of Write() which accepts
  329.   // a string service name or number
  330.   char pszRemoteService[18];
  331.   _itoa(nRemotePort, pszRemoteService, 10);
  332.   return Write(nLen, pData, pszRemoteName, pszRemoteService);
  333. }
  334.  
  335. /////////////////////////////////////////////////////////////////////////////
  336. // CDatagramSocket::Write()
  337. //
  338. // Write data to the socket specified by the name and service
  339. // name or number.
  340. //
  341. // This version of the Write() function takes an integer
  342. // representing the length of the data to send, a pointer
  343. // to the data to send, a pointer to a string representing
  344. // the host name to send the data to, and a string representing
  345. // the service name or port number to send the data to.
  346. //
  347. // The data pointed to by pData must remain valid until either
  348. // the Write() function returns with an error, or the
  349. // write's completion is notified by the m_uMsg being sent
  350. // to the window that owns this datagram object with wParam set
  351. // to CWINSOCK_DONE_WRITING or CWINSOCK_ERROR_WRITING.
  352. //
  353. int CDatagramSocket::Write(int nLen, LPVOID pData,
  354.  LPSTR pszRemoteName, LPSTR pszRemoteService)
  355. {
  356.   int nStatus = CWINSOCK_NOERROR; // error status
  357.   LPHOSTENT pHent;                // pointer to host entry structure
  358.   LPSERVENT pSent;                // pointer to service entry structure
  359.   SOCKADDR_IN sinRemote;          // Internet address of destination
  360.  
  361.   while (1)
  362.   {
  363.     // assign the address family
  364.     sinRemote.sin_family = AF_INET;
  365.  
  366.     // assign the service port (may have to do a database lookup
  367.     // if a service port number was not specified)
  368.     sinRemote.sin_port = htons(atoi(pszRemoteService));
  369.     if (sinRemote.sin_port == 0)
  370.     {
  371.       pSent = getservbyname(pszRemoteService, "udp");
  372.       if (pSent == NULL)
  373.       {
  374.         m_nLastError = WSAGetLastError();
  375.         nStatus = CWINSOCK_WINSOCK_ERROR;
  376.         break;
  377.       }
  378.       sinRemote.sin_port = pSent->s_port;
  379.     }
  380.  
  381.     // assign the IP address (may have to do a database lookup
  382.     // if a dotted decimal IP address was not specified)
  383.     sinRemote.sin_addr.s_addr = inet_addr(pszRemoteName);
  384.     if (sinRemote.sin_addr.s_addr == INADDR_NONE)
  385.     {
  386.       pHent = gethostbyname(pszRemoteName);
  387.       if (pHent == NULL)
  388.       {
  389.         m_nLastError = WSAGetLastError();
  390.         nStatus = CWINSOCK_WINSOCK_ERROR;
  391.         break;
  392.       }
  393.       sinRemote.sin_addr.s_addr = *(u_long *)pHent->h_addr;
  394.     }
  395.  
  396.     // call the version of Write() that takes an
  397.     // Internet address structure
  398.     return Write(nLen, pData, &sinRemote);
  399.   }
  400.  
  401.   return nStatus;
  402. }
  403.  
  404. /////////////////////////////////////////////////////////////////////////////
  405. // CDatagramSocket::Write()
  406. //
  407. // Write data to the socket specified by the Internet address.
  408. //
  409. // This version of the Write() function takes an integer
  410. // representing the length of the data to send, a pointer
  411. // to the data to send, and a pointer to an Internet address
  412. // structure to send the data to.
  413. //
  414. // The data pointed to by pData must remain valid until either
  415. // the Write() function returns with an error, or the
  416. // write's completion is notified by the m_uMsg being sent
  417. // to the window that owns this datagram object with wParam set
  418. // to CWINSOCK_DONE_WRITING or CWINSOCK_ERROR_WRITING.
  419. //
  420. int CDatagramSocket::Write(int nLen, LPVOID pData, LPSOCKADDR_IN psinRemote)
  421. {
  422.   int nStatus = CWINSOCK_NOERROR;
  423.  
  424.   while (1)
  425.   {
  426.     // dynamically allocate a structure to hold the
  427.     // data pointer, the data's length, and the destination address
  428.     LPDATAGRAMDATA pDatagramData = new DATAGRAMDATA;
  429.     if (pDatagramData == NULL)
  430.     {
  431.       nStatus = CWINSOCK_WINDOWS_ERROR;
  432.       break;
  433.     }
  434.     pDatagramData->pData = pData;
  435.     pDatagramData->nLen = nLen;
  436.     memcpy(&(pDatagramData->sin), psinRemote, sizeof(SOCKADDR_IN));
  437.   
  438.     // add the data to the list
  439.     TRY
  440.     {
  441.       m_listWrite.AddTail(pDatagramData);
  442.     }
  443.     CATCH (CMemoryException, e)
  444.     {
  445.       nStatus = CWINSOCK_WINDOWS_ERROR;
  446.       break;
  447.     }
  448.     END_CATCH
  449.  
  450.     // trigger the FD_WRITE handler to try to send
  451.     PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s, WSAMAKESELECTREPLY(FD_WRITE, 0));
  452.     break;
  453.   }
  454.  
  455.   return nStatus;
  456. }
  457.  
  458. /////////////////////////////////////////////////////////////////////////////
  459. // CDatagramSocket::Read()
  460. //
  461. // Read data that has been received by the socket.
  462. //
  463. // This function takes a pointer to an integer that will be filled
  464. // with the length of the data read and an optional pointer
  465. // to an Internet address structure that will be filled with
  466. // the address of the sender of the data.
  467. // 
  468. // A pointer to the data is returned on success.  The application
  469. // using this object must free this pointer.  NULL is returned on failure.
  470. //
  471. LPVOID CDatagramSocket::Read(LPINT pnLen, LPSOCKADDR_IN psinRemote/*= NULL*/)
  472. {
  473.   LPVOID pData = NULL;
  474.  
  475.   // check to see if there is data to retrieve
  476.   if (!m_listRead.IsEmpty())
  477.   {
  478.     // remove the stream data from the list
  479.     LPDATAGRAMDATA pDatagramData = (LPDATAGRAMDATA)m_listRead.RemoveHead();
  480.     pData = pDatagramData->pData;
  481.     *pnLen = pDatagramData->nLen;
  482.     if (psinRemote != NULL)
  483.       memcpy(psinRemote, &(pDatagramData->sin), sizeof(SOCKADDR_IN));
  484.     delete pDatagramData;
  485.   }
  486.  
  487.   return pData;
  488. }
  489.  
  490. // message map
  491. BEGIN_MESSAGE_MAP(CDatagramSocket, CWnd)
  492.   //{{AFX_MSG_MAP(CDatagramSocket)
  493.   //}}AFX_MSG_MAP
  494.   ON_MESSAGE(CWINSOCK_EVENT_NOTIFICATION, OnWinSockEvent)
  495. END_MESSAGE_MAP()
  496.  
  497. /////////////////////////////////////////////////////////////////////////////
  498. // CDatagramSocket::OnWinSockEvent()
  499. //
  500. // Called when there is an asynchronous event on the socket.
  501. //
  502. LONG CDatagramSocket::OnWinSockEvent(WPARAM wParam, LPARAM lParam)
  503. {
  504.   // check for an error
  505.   if (WSAGETSELECTERROR(lParam) != 0)
  506.     return 0L;
  507.  
  508.   // what event are we being notified of?
  509.   switch (WSAGETSELECTEVENT(lParam))
  510.   {
  511.     case FD_READ:
  512.       return HandleRead(wParam, lParam);
  513.       break;
  514.     case FD_WRITE:
  515.       return HandleWrite(wParam, lParam);
  516.       break;
  517.     default:
  518.       // this should never happen
  519.       ASSERT(0);
  520.       break;
  521.   }
  522.  
  523.   return 0L;
  524. }
  525.  
  526. /////////////////////////////////////////////////////////////////////////////
  527. // CDatagramSocket::HandleRead()
  528. //
  529. // Called when there is an asynchronous read event on the socket.
  530. //
  531. // If the read was successful, the data, its length, and the address
  532. // of the sender of the data, are stored in the read queue.  Upon
  533. // a successful read, the application window using this object is
  534. // then notified with the m_uMsg message (wParam set to
  535. // CWINSOCK_DONE_READING; lParam set to the number of data chunks
  536. // in the read queue).  At this point, the application should call
  537. // Read(). If the read fails for some reason, the m_uMsg is sent
  538. // with wParam set to CWINSOCK_ERROR_READING.
  539. //
  540. LONG CDatagramSocket::HandleRead(WPARAM wParam, LPARAM lParam)
  541. {
  542.   while (1)
  543.   {
  544.     // allocate memory for incoming data
  545.     LPVOID pData = malloc(READ_BUF_LEN);
  546.     LPDATAGRAMDATA pDatagramData = new DATAGRAMDATA;
  547.     if ((pData == NULL) || (pDatagramData == NULL))
  548.     {
  549.       // free anything that was allocated
  550.       if (pData != NULL)
  551.         free(pData);
  552.       pData = NULL;
  553.       if (pDatagramData != NULL)
  554.         delete pDatagramData;
  555.       pDatagramData = NULL;
  556.  
  557.       // tell the parent that a possible data read failed
  558.       m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);
  559.  
  560.       // fake the event to try again
  561.       PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s,
  562.        WSAMAKESELECTREPLY(FD_READ, 0));
  563.  
  564.       break;
  565.     }
  566.  
  567.     // receive data
  568.     int nAddrLen = sizeof(SOCKADDR_IN);
  569.     int nBytesRead = recvfrom(m_s, (LPSTR)pData, READ_BUF_LEN, 0,
  570.      (LPSOCKADDR)&(pDatagramData->sin), &nAddrLen);
  571.     if (nBytesRead == SOCKET_ERROR)
  572.     {
  573.       // free memory for incoming data
  574.       free(pData);
  575.       pData = NULL;
  576.       delete pDatagramData;
  577.       pDatagramData = NULL;
  578.  
  579.       // if the error is just that the read would block,
  580.       // don't do anything; we'll get another FD_READ soon
  581.       m_nLastError = WSAGetLastError();
  582.       if (m_nLastError == WSAEWOULDBLOCK)
  583.         m_nLastError = 0;
  584.       else
  585.         // tell the parent that a data read failed
  586.         m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);
  587.  
  588.       break;
  589.     }
  590.  
  591.     // add the data to the list
  592.     pDatagramData->pData = pData;
  593.     pDatagramData->nLen = nBytesRead;
  594.     TRY
  595.     {
  596.       m_listRead.AddTail(pDatagramData);
  597.     }
  598.     CATCH (CMemoryException, e)
  599.     {
  600.       free(pData);
  601.       pData = NULL;
  602.       delete pDatagramData;
  603.       pDatagramData = NULL;
  604.       // tell the parent that a data read failed
  605.       m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);
  606.       break;
  607.     }
  608.     END_CATCH
  609.  
  610.     // tell the parent that data has been read
  611.     m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_DONE_READING,
  612.      (LPARAM)m_listRead.GetCount());
  613.     
  614.     break;
  615.   }
  616.  
  617.   return 0L;
  618. }
  619.  
  620. /////////////////////////////////////////////////////////////////////////////
  621. // CDatagramSocket::HandleWrite()
  622. //
  623. // Called when there is an asynchronous write event on the socket.
  624. //
  625. // If there is data in the write queue waiting to be sent,
  626. // a WinSock send is attempted.  If the send is successful,
  627. // a m_uMsg message is sent to the application window with
  628. // wParam set to CWINSOCK_DONE_WRITING and lParam set to the
  629. // address of the data that was sent.  On send failure,
  630. // wParam is set to CWINSOCK_ERROR_WRITING and lParam set to
  631. // the address of the data which couldn't be sent.  In either
  632. // case, the application may free the pointer pointing to
  633. // the data or reuse that data buffer.
  634. //
  635. LONG CDatagramSocket::HandleWrite(WPARAM wParam, LPARAM lParam)
  636. {
  637.   while (1)
  638.   {
  639.     // check to see if there is any data to send
  640.     if (m_listWrite.IsEmpty())
  641.       break;
  642.  
  643.     // get pointers to data, data length, and destination address
  644.     LPDATAGRAMDATA pDatagramData = (LPDATAGRAMDATA)m_listWrite.GetHead();
  645.     LPVOID pData = pDatagramData->pData;
  646.     int nLen = pDatagramData->nLen;
  647.     SOCKADDR_IN sin;
  648.     memcpy(&sin, &(pDatagramData->sin), sizeof(SOCKADDR_IN));
  649.  
  650.     // send the data
  651.     BOOL bRemove = FALSE;      // remove data from queue?
  652.     int nBytesSent = sendto(m_s, (LPCSTR)pData, nLen, 0,
  653.      (LPSOCKADDR)&sin, sizeof(SOCKADDR_IN));
  654.     if (nBytesSent == SOCKET_ERROR)
  655.     {
  656.       // if the error is just that the send would block,
  657.       // don't do anything; we'll get another FD_WRITE soon
  658.       m_nLastError = WSAGetLastError();
  659.       if (m_nLastError == WSAEWOULDBLOCK)
  660.         m_nLastError = 0;
  661.       else
  662.       {
  663.         bRemove = TRUE;
  664.         m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
  665.          (LPARAM)pData);
  666.       }
  667.     }
  668.     else
  669.     {
  670.       // if data was sent, we must still check to see
  671.       // if all the bytes were sent
  672.       bRemove = TRUE;
  673.       if (nBytesSent == nLen)
  674.         m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_DONE_WRITING,
  675.          (LPARAM)pData);
  676.       else
  677.         m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
  678.          (LPARAM)pData);
  679.     }
  680.  
  681.     // if the data was sent or there was a real
  682.     // error, remove the data from the queue
  683.     if (bRemove)
  684.     {
  685.       delete pDatagramData;
  686.       m_listWrite.RemoveHead();
  687.     }
  688.  
  689.     // if there is more data to send, trigger this FD_WRITE handler
  690.     if (!m_listWrite.IsEmpty())
  691.       PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s,
  692.        WSAMAKESELECTREPLY(FD_WRITE, 0));
  693.  
  694.     break;
  695.   }
  696.  
  697.   return 0L;
  698. }
  699.  
  700. /////////////////////////////////////////////////////////////////////////////
  701. // CStreamSocket
  702. /////////////////////////////////////////////////////////////////////////////
  703.  
  704. /////////////////////////////////////////////////////////////////////////////
  705. // CStreamSocket constructor
  706. //
  707. // Constructs the CStreamSocket object. Initializes member variables
  708. //
  709. CStreamSocket::CStreamSocket(CWnd *pParentWnd, UINT uMsg)
  710. {
  711.   m_pParentWnd = pParentWnd;
  712.   ASSERT(m_pParentWnd != NULL);
  713.   m_uMsg = uMsg;
  714.   ASSERT(m_uMsg != 0);
  715.   InitVars();
  716. }
  717.  
  718. /////////////////////////////////////////////////////////////////////////////
  719. // CStreamSocket destructor
  720. //
  721. CStreamSocket::~CStreamSocket()
  722. {
  723. }
  724.  
  725. /////////////////////////////////////////////////////////////////////////////
  726. // CStreamSocket::InitVars()
  727. //
  728. // Initialize class member variables.
  729. //
  730. void CStreamSocket::InitVars(BOOL bInitLastError/*= TRUE*/)
  731. {
  732.   if (bInitLastError)
  733.     m_nLastError = 0;
  734.  
  735.   m_s = INVALID_SOCKET;
  736.   memset(&m_sinLocal, 0, sizeof(SOCKADDR_IN));
  737.   memset(&m_sinRemote, 0, sizeof(SOCKADDR_IN));
  738.   m_bServer = FALSE;
  739. }
  740.  
  741. /////////////////////////////////////////////////////////////////////////////
  742. // CStreamSocket::CreateSocket()
  743. //
  744. // Create a hidden window that will receive asynchronous messages
  745. // from WinSock.  Also creates a socket and optionally binds it to
  746. // a name if the socket is a server socket.
  747. //
  748. // This version of the CreateSocket() function takes a
  749. // port number, in host order, as input.  A port number
  750. // should only be specified if the socket is to be bound
  751. // to a certain port.  If you don't care which port is
  752. // assigned to the socket, just call CreateSocket() without
  753. // any parameter, causing CreateSocket(NULL) to be called.
  754. //
  755. int CStreamSocket::CreateSocket(int nLocalPort)
  756. {
  757.   // if this version of the function is being called,
  758.   // a valid port number must be specified
  759.   if (nLocalPort <= 0)
  760.     return CWINSOCK_PROGRAMMING_ERROR;
  761.  
  762.   // convert the port number into a string and
  763.   // call the version of CreateSocket() which
  764.   // accepts a string
  765.   char pszLocalService[18];
  766.   _itoa(nLocalPort, pszLocalService, 10);
  767.   return CreateSocket(pszLocalService);
  768. }
  769.  
  770. /////////////////////////////////////////////////////////////////////////////
  771. // CStreamSocket::CreateSocket()
  772. //
  773. // Create a hidden window that will receive asynchronous messages
  774. // from WinSock.  Also creates a socket and optionally binds it to
  775. // a name if the socket is a server socket.
  776. //
  777. // This version of the CreateSocket() function takes a
  778. // string containing a service name or port number.
  779. // A parameter should only be specified if the socket is to be
  780. // bound to a certain port.  If you don't care which port is
  781. // assigned to the socket, just call CreateSocket() without
  782. // any parameter, causing CreateSocket(NULL) to be called.
  783. //
  784. int CStreamSocket::CreateSocket(LPSTR pszLocalService/*= NULL*/)
  785. {
  786.   int nStatus = CWINSOCK_NOERROR;
  787.  
  788.   while (1)
  789.   {
  790.     // Make sure the socket isn't already created.
  791.     // If the socket handle is valid, return from this
  792.     // function right away so the existing parameters of
  793.     // the object are not tampered with.
  794.     if (m_s != INVALID_SOCKET)
  795.       return CWINSOCK_PROGRAMMING_ERROR;
  796.  
  797.     InitVars();
  798.  
  799.     // create the hidden window
  800.     RECT rect;
  801.     rect.left = 0;
  802.     rect.top = 0;
  803.     rect.right = 100;
  804.     rect.bottom = 100;
  805.     if (Create(NULL, NULL, WS_OVERLAPPEDWINDOW, rect, m_pParentWnd, 0) == 0)
  806.     {
  807.       nStatus = CWINSOCK_WINDOWS_ERROR;
  808.       break;
  809.     }
  810.  
  811.     // create the socket
  812.     m_s = socket(PF_INET, SOCK_STREAM, 0);
  813.     if (m_s == INVALID_SOCKET)
  814.     {
  815.       m_nLastError = WSAGetLastError();
  816.       nStatus = CWINSOCK_WINSOCK_ERROR;
  817.       DestroyWindow();
  818.       break;
  819.     }
  820.  
  821.     // If pszLocalService is not NULL, this is a server socket
  822.     // that will accept data on the specified port.
  823.     if (pszLocalService != NULL)
  824.     {
  825.       // this socket is bound to a port number
  826.       // so set the server flag
  827.       m_bServer = TRUE;
  828.  
  829.       // assign the address family
  830.       m_sinLocal.sin_family = AF_INET;
  831.  
  832.       // assign the service port (may have to do a database lookup
  833.       // if a service port number was not specified)
  834.       m_sinLocal.sin_port = htons(atoi(pszLocalService));
  835.       if (m_sinLocal.sin_port == 0)
  836.       {
  837.         LPSERVENT pSent = getservbyname(pszLocalService, "tcp");
  838.         if (pSent == NULL)
  839.         {
  840.           m_nLastError = WSAGetLastError();
  841.           nStatus = CWINSOCK_WINSOCK_ERROR;
  842.           closesocket(m_s);
  843.           DestroyWindow();
  844.           break;
  845.         }
  846.         m_sinLocal.sin_port = pSent->s_port;
  847.       }
  848.  
  849.       // assign the IP address
  850.       m_sinLocal.sin_addr.s_addr = htonl(INADDR_ANY);
  851.  
  852.       // bind the server socket to the name containing the port
  853.       if (bind(m_s, (LPSOCKADDR)&m_sinLocal, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
  854.       {
  855.         m_nLastError = WSAGetLastError();
  856.         nStatus = CWINSOCK_WINSOCK_ERROR;
  857.         closesocket(m_s);
  858.         DestroyWindow();
  859.         break;
  860.       }
  861.     }
  862.  
  863.     // start asynchronous event notification
  864.     long lEvent;
  865.     if (m_bServer)
  866.       lEvent = FD_READ | FD_WRITE | FD_ACCEPT | FD_CLOSE;
  867.     else
  868.       lEvent = FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE;
  869.     if (WSAAsyncSelect(m_s, m_hWnd, CWINSOCK_EVENT_NOTIFICATION, lEvent) ==
  870.      SOCKET_ERROR)
  871.     {
  872.       m_nLastError = WSAGetLastError();
  873.       nStatus = CWINSOCK_WINSOCK_ERROR;
  874.       closesocket(m_s);
  875.       DestroySocket();
  876.       break;
  877.     }
  878.  
  879.     // if this is a server, listen for client connections
  880.     if (m_bServer)
  881.     {
  882.       if (listen(m_s, 3) == SOCKET_ERROR)
  883.       {
  884.         m_nLastError = WSAGetLastError();
  885.         nStatus = CWINSOCK_WINSOCK_ERROR;
  886.         closesocket(m_s);
  887.         DestroySocket();
  888.         break;
  889.       }
  890.     }
  891.  
  892.     break;
  893.   }
  894.  
  895.   // if anything failed in this function, set the
  896.   // socket variables appropriately
  897.   if (nStatus != CWINSOCK_NOERROR)
  898.     InitVars(FALSE);
  899.  
  900.   return nStatus;
  901. }
  902.  
  903. /////////////////////////////////////////////////////////////////////////////
  904. // CStreamSocket::DestroySocket()
  905. //
  906. // Close the socket, remove any queued data,
  907. // and destroy the hidden window.
  908. //
  909. int CStreamSocket::DestroySocket()
  910. {
  911.   int nStatus = CWINSOCK_NOERROR;
  912.  
  913.   // make sure the socket is valid
  914.   if (m_s == INVALID_SOCKET)
  915.     nStatus = CWINSOCK_PROGRAMMING_ERROR;
  916.   else
  917.   {
  918.     // remove any data in the write queue
  919.     while (!m_listWrite.IsEmpty())
  920.     {
  921.       LPSTREAMDATA pStreamData = (LPSTREAMDATA)m_listWrite.RemoveHead();
  922.       LPVOID pData = pStreamData->pData;
  923.       delete pStreamData;
  924.  
  925.       m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
  926.        (LPARAM)pData);
  927.     }
  928.  
  929.     // remove any data in the read queue
  930.     while (!m_listRead.IsEmpty())
  931.     {
  932.       LPSTREAMDATA pStreamData = (LPSTREAMDATA)m_listRead.RemoveHead();
  933.       free(pStreamData->pData);
  934.       delete pStreamData;
  935.     }
  936.  
  937.     // close the socket and initialize variables
  938.     closesocket(m_s);
  939.     InitVars();
  940.  
  941.     // destroy the hidden window
  942.     DestroyWindow();
  943.   }
  944.  
  945.   return nStatus;
  946. }
  947.  
  948. /////////////////////////////////////////////////////////////////////////////
  949. // CStreamSocket::Connect()
  950. //
  951. // Connect the client socket to a server specified by the name and port.
  952. //
  953. // This version of the Conncet() function takes a pointer to a
  954. // string representing the host name to send the data to and
  955. // an integer representing the port number to connect to.
  956. //
  957. int CStreamSocket::Connect(LPSTR pszRemoteName, int nRemotePort)
  958. {
  959.   // convert the port number into a string and
  960.   // call the version of Connect() which accepts
  961.   // a string service name or number
  962.   char pszRemoteService[18];
  963.   _itoa(nRemotePort, pszRemoteService, 10);
  964.   return Connect(pszRemoteName, pszRemoteService);
  965. }
  966.  
  967. /////////////////////////////////////////////////////////////////////////////
  968. // CStreamSocket::Connect()
  969. //
  970. // Connect the client socket to a server specified by the name and
  971. // service name or port.
  972. //
  973. // This version of the Connect() function takes a pointer to a
  974. // string representing the host name to send the data to and
  975. // an integer representing the service name or port number to
  976. // connect to.
  977. //
  978. int CStreamSocket::Connect(LPSTR pszRemoteName, LPSTR pszRemoteService)
  979. {
  980.   int nStatus = CWINSOCK_NOERROR; // error status
  981.   LPHOSTENT pHent;                // pointer to host entry structure
  982.   LPSERVENT pSent;                // pointer to service entry structure
  983.   SOCKADDR_IN sinRemote;          // Internet address of destination
  984.  
  985.   while (1)
  986.   {
  987.     // assign the address family
  988.     sinRemote.sin_family = AF_INET;
  989.  
  990.     // assign the service port (may have to do a database lookup
  991.     // if a service port number was not specified)
  992.     sinRemote.sin_port = htons(atoi(pszRemoteService));
  993.     if (sinRemote.sin_port == 0)
  994.     {
  995.       pSent = getservbyname(pszRemoteService, "tcp");
  996.       if (pSent == NULL)
  997.       {
  998.         m_nLastError = WSAGetLastError();
  999.         nStatus = CWINSOCK_WINSOCK_ERROR;
  1000.         break;
  1001.       }
  1002.       sinRemote.sin_port = pSent->s_port;
  1003.     }
  1004.  
  1005.     // assign the IP address (may have to do a database lookup
  1006.     // if a dotted decimal IP address was not specified)
  1007.     sinRemote.sin_addr.s_addr = inet_addr(pszRemoteName);
  1008.     if (sinRemote.sin_addr.s_addr == INADDR_NONE)
  1009.     {
  1010.       pHent = gethostbyname(pszRemoteName);
  1011.       if (pHent == NULL)
  1012.       {
  1013.         m_nLastError = WSAGetLastError();
  1014.         nStatus = CWINSOCK_WINSOCK_ERROR;
  1015.         break;
  1016.       }
  1017.       sinRemote.sin_addr.s_addr = *(u_long *)pHent->h_addr;
  1018.     }
  1019.  
  1020.     // call the version of Connect() that takes an
  1021.     // Internet address structure
  1022.     return Connect(&sinRemote);
  1023.   }
  1024.  
  1025.   return nStatus;
  1026. }
  1027.  
  1028. /////////////////////////////////////////////////////////////////////////////
  1029. // CStreamSocket::Connect()
  1030. //
  1031. // Connect the client socket to a server specified by the
  1032. // Internet address.
  1033. //
  1034. // This version of the Connect() function takes a pointer
  1035. // to an Internet address structure to connect to.
  1036. //
  1037. int CStreamSocket::Connect(LPSOCKADDR_IN psinRemote)
  1038. {
  1039.   int nStatus = CWINSOCK_NOERROR;
  1040.  
  1041.   while (1)
  1042.   {
  1043.     // only clients should call connect
  1044.     if (m_bServer)
  1045.     {
  1046.       nStatus = CWINSOCK_PROGRAMMING_ERROR;
  1047.       break;
  1048.     }
  1049.  
  1050.     // copy the Internet address of the remote server to connect to
  1051.     memcpy(&m_sinRemote, psinRemote, sizeof(SOCKADDR_IN));
  1052.  
  1053.     // attempt the asynchronous connect
  1054.     if (connect(m_s, (LPSOCKADDR)&m_sinRemote, sizeof(SOCKADDR_IN)) ==
  1055.      SOCKET_ERROR)
  1056.     {
  1057.       m_nLastError = WSAGetLastError();
  1058.       if (m_nLastError == WSAEWOULDBLOCK)
  1059.         m_nLastError = 0;
  1060.       else
  1061.         nStatus = CWINSOCK_WINSOCK_ERROR;
  1062.       break;
  1063.     }
  1064.  
  1065.     break;
  1066.   }
  1067.  
  1068.   return nStatus;
  1069. }
  1070.  
  1071. /////////////////////////////////////////////////////////////////////////////
  1072. // CStreamSocket::Accept()
  1073. //
  1074. // Accept a connection request from a client.
  1075. //
  1076. // This function takes a pointer to a CStreamSocket object. This
  1077. // pointer will become the newly connected socket.
  1078. //
  1079. int CStreamSocket::Accept(CStreamSocket *pStreamSocket)
  1080. {
  1081.   int nStatus = CWINSOCK_NOERROR;
  1082.  
  1083.   while (1)
  1084.   {
  1085.     // must have valid CStreamSocket object pointer passed in
  1086.     if (pStreamSocket == NULL)
  1087.     {
  1088.       ASSERT(0);
  1089.       nStatus = CWINSOCK_PROGRAMMING_ERROR;
  1090.       break;
  1091.     }
  1092.  
  1093.     // only servers should call accept
  1094.     if (!m_bServer)
  1095.     {
  1096.       nStatus = CWINSOCK_PROGRAMMING_ERROR;
  1097.       break;
  1098.     }
  1099.  
  1100.     // Make sure the socket isn't already created.
  1101.     // If the socket handle is valid, return from this
  1102.     // function right away so the existing parameters of
  1103.     // the object are not tampered with.
  1104.     if (pStreamSocket->m_s != INVALID_SOCKET)
  1105.       return CWINSOCK_PROGRAMMING_ERROR;
  1106.  
  1107.     pStreamSocket->InitVars();
  1108.  
  1109.     // create the hidden window
  1110.     RECT rect;
  1111.     rect.left = 0;
  1112.     rect.top = 0;
  1113.     rect.right = 100;
  1114.     rect.bottom = 100;
  1115.     if (pStreamSocket->Create(NULL, NULL, WS_OVERLAPPEDWINDOW, rect,
  1116.      pStreamSocket->m_pParentWnd, 0) == 0)
  1117.     {
  1118.       nStatus = CWINSOCK_WINDOWS_ERROR;
  1119.       break;
  1120.     }
  1121.  
  1122.     // accept the client connection
  1123.     pStreamSocket->m_s = accept(m_s, NULL, NULL);
  1124.     if (pStreamSocket->m_s == INVALID_SOCKET)
  1125.     {
  1126.       m_nLastError = WSAGetLastError();
  1127.       nStatus = CWINSOCK_WINSOCK_ERROR;
  1128.       pStreamSocket->DestroyWindow();
  1129.       break;
  1130.     }
  1131.  
  1132.     // start asynchronous event notification
  1133.     long lEvent;
  1134.     lEvent = FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE;
  1135.     if (WSAAsyncSelect(pStreamSocket->m_s, pStreamSocket->m_hWnd,
  1136.      CWINSOCK_EVENT_NOTIFICATION, lEvent) == SOCKET_ERROR)
  1137.     {
  1138.       m_nLastError = WSAGetLastError();
  1139.       nStatus = CWINSOCK_WINSOCK_ERROR;
  1140.       closesocket(pStreamSocket->m_s);
  1141.       pStreamSocket->DestroySocket();
  1142.       break;
  1143.     }
  1144.  
  1145.     break;
  1146.   }
  1147.  
  1148.   // if anything failed in this function, set the
  1149.   // socket variables appropriately
  1150.   if (nStatus == CWINSOCK_WINSOCK_ERROR)
  1151.     pStreamSocket->InitVars(FALSE);
  1152.   else if (nStatus == CWINSOCK_NOERROR)
  1153.     // notify the parent if the connection was accepted successfully
  1154.     pStreamSocket->m_pParentWnd->PostMessage(pStreamSocket->m_uMsg,
  1155.      CWINSOCK_YOU_ARE_CONNECTED);
  1156.  
  1157.   return nStatus;
  1158. }
  1159.  
  1160. /////////////////////////////////////////////////////////////////////////////
  1161. // CStreamSocket::Write()
  1162. //
  1163. // Write data to the socket..
  1164. //
  1165. // This function takes an integer representing the length of the
  1166. // data to send and a pointer to the data to send.
  1167. //
  1168. // The data pointed to by pData must remain valid until either
  1169. // the Write() function returns with an error, or the
  1170. // write's completion is notified by the m_uMsg being sent
  1171. // to the window that owns this datagram object with wParam set
  1172. // to CWINSOCK_DONE_WRITING or CWINSOCK_ERROR_WRITING.
  1173. //
  1174. int CStreamSocket::Write(int nLen, LPVOID pData)
  1175. {
  1176.   int nStatus = CWINSOCK_NOERROR;
  1177.  
  1178.   while (1)
  1179.   {
  1180.     // dynamically allocate a structure to hold the
  1181.     // data pointer and the data's length
  1182.     LPSTREAMDATA pStreamData = new STREAMDATA;
  1183.     if (pStreamData == NULL)
  1184.     {
  1185.       nStatus = CWINSOCK_WINDOWS_ERROR;
  1186.       break;
  1187.     }
  1188.     pStreamData->pData = pData;
  1189.     pStreamData->nLen = nLen;
  1190.  
  1191.     // add the data to the list
  1192.     TRY
  1193.     {
  1194.       m_listWrite.AddTail(pStreamData);
  1195.     }
  1196.     CATCH (CMemoryException, e)
  1197.     {
  1198.       delete pStreamData;
  1199.       nStatus = CWINSOCK_WINDOWS_ERROR;
  1200.       break;
  1201.     }
  1202.     END_CATCH
  1203.  
  1204.     // trigger the FD_WRITE handler to try to send
  1205.     PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s, WSAMAKESELECTREPLY(FD_WRITE, 0));
  1206.     break;
  1207.   }
  1208.  
  1209.   return nStatus;
  1210. }
  1211.  
  1212. /////////////////////////////////////////////////////////////////////////////
  1213. // CStreamSocket::Read()
  1214. //
  1215. // Read data that has been received by the socket.
  1216. //
  1217. // This function takes a pointer to an integer that will be filled
  1218. // with the length of the data read.
  1219. // 
  1220. // A pointer to the data is returned on success.  The application
  1221. // using this object must free this pointer.  NULL is returned on failure.
  1222. //
  1223. LPVOID CStreamSocket::Read(LPINT pnLen)
  1224. {
  1225.   LPVOID pData = NULL;
  1226.  
  1227.   // check to see if there is data to retrieve
  1228.   if (!m_listRead.IsEmpty())
  1229.   {
  1230.     // remove the stream data from the list
  1231.     LPSTREAMDATA pStreamData = (LPSTREAMDATA)m_listRead.RemoveHead();
  1232.     pData = pStreamData->pData;
  1233.     *pnLen = pStreamData->nLen;
  1234.     delete pStreamData;
  1235.   }
  1236.  
  1237.   return pData;
  1238. }
  1239.  
  1240. // message map
  1241. BEGIN_MESSAGE_MAP(CStreamSocket, CWnd)
  1242.   //{{AFX_MSG_MAP(CStreamSocket)
  1243.   //}}AFX_MSG_MAP
  1244.   ON_MESSAGE(CWINSOCK_EVENT_NOTIFICATION, OnWinSockEvent)
  1245. END_MESSAGE_MAP()
  1246.  
  1247. /////////////////////////////////////////////////////////////////////////////
  1248. // CStreamSocket::OnWinSockEvent()
  1249. //
  1250. // Called when there is an asynchronous event on the socket.
  1251. //
  1252. LONG CStreamSocket::OnWinSockEvent(WPARAM wParam, LPARAM lParam)
  1253. {
  1254.   // check for an error
  1255.   if (WSAGETSELECTERROR(lParam) != 0)
  1256.     return 0L;
  1257.  
  1258.   // what event are we being notified of?
  1259.   switch (WSAGETSELECTEVENT(lParam))
  1260.   {
  1261.     case FD_READ:
  1262.       return HandleRead(wParam, lParam);
  1263.       break;
  1264.     case FD_WRITE:
  1265.       return HandleWrite(wParam, lParam);
  1266.       break;
  1267.     case FD_ACCEPT:
  1268.       // tell the parent window that a client would like to connect
  1269.       // to the server socket
  1270.       m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_READY_TO_ACCEPT_CONNECTION);
  1271.       break;
  1272.     case FD_CONNECT:
  1273.       // tell the parent window that the socket has connected
  1274.       m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_YOU_ARE_CONNECTED);
  1275.       break;
  1276.     case FD_CLOSE:
  1277.       // check for more data queued on the socket
  1278.       // (don't tell the application that the socket is closed
  1279.       // until all data has been read and notification has been posted)
  1280.       if (HandleRead(wParam, lParam))
  1281.       {
  1282.         // fake the close event to try again
  1283.         PostMessage(CWINSOCK_EVENT_NOTIFICATION, wParam, lParam);
  1284.         break;
  1285.       }
  1286.       
  1287.       // tell the parent window that the socket is closed
  1288.       m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_LOST_CONNECTION);
  1289.       break;
  1290.     default:
  1291.       // this should never happen
  1292.       ASSERT(0);
  1293.       break;
  1294.   }
  1295.  
  1296.   return 0L;
  1297. }
  1298.  
  1299. /////////////////////////////////////////////////////////////////////////////
  1300. // CStreamSocket::HandleRead()
  1301. //
  1302. // Called when there is an asynchronous read event on the socket.
  1303. //
  1304. // If the read was successful, the data and its length are stored
  1305. // in the read queue.  Upon a successful read, the application
  1306. // window using this object is then notified with the m_uMsg message
  1307. // (wParam set to CWINSOCK_DONE_READING; lParam set to the number of
  1308. // data chunks in the read queue).  At this point, the application
  1309. // should call Read(). If the read fails for some reason, the m_uMsg
  1310. // is sent with wParam set to CWINSOCK_ERROR_READING.
  1311. //
  1312. LONG CStreamSocket::HandleRead(WPARAM wParam, LPARAM lParam)
  1313. {
  1314.   while (1)
  1315.   {
  1316.     // allocate memory for incoming data
  1317.     LPVOID pData = malloc(READ_BUF_LEN);
  1318.     LPSTREAMDATA pStreamData = new STREAMDATA;
  1319.     if ((pData == NULL) || (pStreamData == NULL))
  1320.     {
  1321.       // free anything that was allocated
  1322.       if (pData != NULL)
  1323.         free(pData);
  1324.       pData = NULL;
  1325.       if (pStreamData != NULL)
  1326.         delete pStreamData;
  1327.       pStreamData = NULL;
  1328.  
  1329.       // tell the parent that a possible data read failed
  1330.       m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);
  1331.  
  1332.       // fake the event to try again
  1333.       PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s,
  1334.        WSAMAKESELECTREPLY(FD_READ, 0));
  1335.  
  1336.       break;
  1337.     }
  1338.  
  1339.     // receive data
  1340.     int nBytesRead = recv(m_s, (LPSTR)pData, READ_BUF_LEN, 0);
  1341.     if (nBytesRead == SOCKET_ERROR)
  1342.     {
  1343.       // free memory for incoming data
  1344.       free(pData);
  1345.       pData = NULL;
  1346.       delete pStreamData;
  1347.       pStreamData = NULL;
  1348.  
  1349.       // if the error is just that the read would block,
  1350.       // don't do anything; we'll get another FD_READ soon
  1351.       m_nLastError = WSAGetLastError();
  1352.       if (m_nLastError == WSAEWOULDBLOCK)
  1353.         m_nLastError = 0;
  1354.       else
  1355.         // tell the parent that a data read failed
  1356.         m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);
  1357.  
  1358.       break;
  1359.     }
  1360.  
  1361.     // make sure some data was read
  1362.     if (nBytesRead == 0)
  1363.     {
  1364.       // free memory for incoming data
  1365.       free(pData);
  1366.       pData = NULL;
  1367.       delete pStreamData;
  1368.       pStreamData = NULL;
  1369.  
  1370.       break;
  1371.     }
  1372.  
  1373.     // add the data to the list
  1374.     pStreamData->pData = pData;
  1375.     pStreamData->nLen = nBytesRead;
  1376.     TRY
  1377.     {
  1378.       m_listRead.AddTail(pStreamData);
  1379.     }
  1380.     CATCH (CMemoryException, e)
  1381.     {
  1382.       free(pData);
  1383.       pData = NULL;
  1384.       delete pStreamData;
  1385.       pStreamData = NULL;
  1386.       // tell the parent that a data read failed
  1387.       m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);
  1388.       break;
  1389.     }
  1390.     END_CATCH
  1391.  
  1392.     // tell the parent that data has been read
  1393.     m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_DONE_READING,
  1394.      (LPARAM)m_listRead.GetCount());
  1395.  
  1396.     // 1 is returned if there is data so CStreamSocket::OnWinSockEvent()'s
  1397.     // FD_CLOSE handler will know when the socket can really be closed
  1398.     return 1L;
  1399.     
  1400.     break;
  1401.   }
  1402.  
  1403.   return 0L;
  1404. }
  1405.  
  1406. /////////////////////////////////////////////////////////////////////////////
  1407. // CStreamSocket::HandleWrite()
  1408. //
  1409. // Called when there is an asynchronous write event on the socket.
  1410. //
  1411. // If there is data in the write queue waiting to be sent,
  1412. // a WinSock send is attempted.  If the send is successful,
  1413. // a m_uMsg message is sent to the application window with
  1414. // wParam set to CWINSOCK_DONE_WRITING and lParam set to the
  1415. // address of the data that was sent.  On send failure,
  1416. // wParam is set to CWINSOCK_ERROR_WRITING and lParam set to
  1417. // the address of the data which couldn't be sent.  In either
  1418. // case, the application may free the pointer pointing to
  1419. // the data or reuse that data buffer.  It is possible for the
  1420. // entire amount of data to not be sent in one call to send().
  1421. // In this case, an attempt is made to send the remaining portion
  1422. // of that block of data the next time HandleWrite() is invoked.
  1423. // 
  1424. //
  1425. LONG CStreamSocket::HandleWrite(WPARAM wParam, LPARAM lParam)
  1426. {
  1427.   LPSTREAMDATA pStreamData;            // pointer to stream data structure
  1428.   LPVOID pData;                        // pointer to buffer to send
  1429.   int nLen;                            // total length of buffer to send
  1430.   static LPVOID pDataRemaining = NULL; // pointer into buffer to send
  1431.   static int nLenRemaining = 0;        // number of bytes left to send
  1432.  
  1433.   while (1)
  1434.   {
  1435.     // check to see if there is any data to send
  1436.     if (m_listWrite.IsEmpty())
  1437.       break;
  1438.  
  1439.     // if we are not in the middle of another buffer send,
  1440.     // get data and data length from the write queue
  1441.     pStreamData = (LPSTREAMDATA)m_listWrite.GetHead(); // not RemoveHead()
  1442.     pData = pStreamData->pData;
  1443.     nLen = pStreamData->nLen;
  1444.     if (pDataRemaining == NULL)
  1445.     {
  1446.       pDataRemaining = pData;
  1447.       nLenRemaining = nLen;
  1448.     }
  1449.  
  1450.     // send the data
  1451.     BOOL bRemove = FALSE;      // remove data from queue?
  1452.     int nBytesSent = send(m_s, (LPCSTR)pDataRemaining, nLenRemaining, 0);
  1453.     if (nBytesSent == SOCKET_ERROR)
  1454.     {
  1455.       // if the error is just that the send would block,
  1456.       // don't do anything; we'll get another FD_WRITE soon
  1457.       m_nLastError = WSAGetLastError();
  1458.       if (m_nLastError == WSAEWOULDBLOCK)
  1459.         m_nLastError = 0;
  1460.       else
  1461.       {
  1462.         bRemove = TRUE;
  1463.         m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
  1464.          (LPARAM)pData);
  1465.       }
  1466.     }
  1467.     else
  1468.     {
  1469.       // if data was sent, we must still check to see
  1470.       // if all the bytes were sent
  1471.       if (nBytesSent == nLenRemaining)
  1472.       {
  1473.         bRemove = TRUE;
  1474.         m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_DONE_WRITING,
  1475.          (LPARAM)pData);
  1476.       }
  1477.       else
  1478.       {
  1479.         // the complete buffer was not sent so adjust
  1480.         // these values accordingly
  1481.         pDataRemaining = (LPVOID)((LPCSTR)pDataRemaining + nBytesSent);
  1482.         nLenRemaining = nLenRemaining - nBytesSent;
  1483.       }
  1484.     }
  1485.  
  1486.     // if the data was completely sent or there was
  1487.     // a real error, remove the data from the queue
  1488.     if (bRemove)
  1489.     {
  1490.       delete pStreamData;
  1491.       m_listWrite.RemoveHead();
  1492.       pDataRemaining = NULL;
  1493.       nLenRemaining = 0;
  1494.     }
  1495.  
  1496.     // if there is more data to send, trigger this FD_WRITE handler
  1497.     if (!m_listWrite.IsEmpty())
  1498.       PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s,
  1499.        WSAMAKESELECTREPLY(FD_WRITE, 0));
  1500.  
  1501.     break;
  1502.   }
  1503.  
  1504.   return 0L;
  1505. }
  1506.  
  1507. /////////////////////////////////////////////////////////////////////////////
  1508. // CStreamSocket::GetPeerName()
  1509. //
  1510. // Copies the Internet address of the other end of the socket
  1511. // connection into the pointer provided.
  1512. // Useful for server's to use after an Accept().
  1513. //
  1514. int CStreamSocket::GetPeerName(LPSOCKADDR_IN psinRemote)
  1515. {
  1516.   int nStatus = CWINSOCK_NOERROR;
  1517.   int nLen = sizeof(SOCKADDR_IN);
  1518.  
  1519.   // make sure the listening socket doesn't call this function
  1520.   if (m_bServer)
  1521.     nStatus = CWINSOCK_PROGRAMMING_ERROR;
  1522.   else if (getpeername(m_s, (LPSOCKADDR)psinRemote, &nLen) == SOCKET_ERROR)
  1523.   {
  1524.     m_nLastError = WSAGetLastError();
  1525.     nStatus = CWINSOCK_WINSOCK_ERROR;
  1526.   }
  1527.  
  1528.   return nStatus;
  1529. }
  1530.  
  1531. /////////////////////////////////////////////////////////////////////////////
  1532. // Utility functions
  1533. /////////////////////////////////////////////////////////////////////////////
  1534.  
  1535. /////////////////////////////////////////////////////////////////////////////
  1536. // CWinSockErrorBox
  1537. //
  1538. void CWinSockErrorBox(int nError, LPSTR pszMessage/*= NULL*/)
  1539. {
  1540. #define ERROR_BUF_LEN (1000)
  1541.   char pszError[ERROR_BUF_LEN];
  1542.  
  1543.   wsprintf(pszError, "WinSock error %d: ", nError);
  1544.  
  1545.   switch (nError)
  1546.   {
  1547.     case WSAEINTR:
  1548.       lstrcat(pszError, "Interrupted system call");
  1549.       break;
  1550.     case WSAEBADF:
  1551.       lstrcat(pszError, "Bad file number");
  1552.       break;
  1553.     case WSAEACCES:
  1554.       lstrcat(pszError, "Permission denied");
  1555.       break;
  1556.     case WSAEFAULT:
  1557.       lstrcat(pszError, "Bad address");
  1558.       break;
  1559.     case WSAEINVAL:
  1560.       lstrcat(pszError, "Invalid argument");
  1561.       break;
  1562.     case WSAEMFILE:
  1563.       lstrcat(pszError, "Too many open files");
  1564.       break;
  1565.     case WSAEWOULDBLOCK:
  1566.       lstrcat(pszError, "Operation would block");
  1567.       break;
  1568.     case WSAEINPROGRESS:
  1569.       lstrcat(pszError, "Operation now in progress");
  1570.       break;
  1571.     case WSAEALREADY:
  1572.       lstrcat(pszError, "Operation already in progress");
  1573.       break;
  1574.     case WSAENOTSOCK:
  1575.       lstrcat(pszError, "Socket operation on non-socket");
  1576.       break;
  1577.     case WSAEDESTADDRREQ:
  1578.       lstrcat(pszError, "Destination address required");
  1579.       break;
  1580.     case WSAEMSGSIZE:
  1581.       lstrcat(pszError, "Message too long");
  1582.       break;
  1583.     case WSAEPROTOTYPE:
  1584.       lstrcat(pszError, "Protocol wrong type for socket");
  1585.       break;
  1586.     case WSAENOPROTOOPT:
  1587.       lstrcat(pszError, "Protocol not available");
  1588.       break;
  1589.     case WSAEPROTONOSUPPORT:
  1590.       lstrcat(pszError, "Protocol not supported");
  1591.       break;
  1592.     case WSAESOCKTNOSUPPORT:
  1593.       lstrcat(pszError, "Socket type not supported");
  1594.       break;
  1595.     case WSAEOPNOTSUPP:
  1596.       lstrcat(pszError, "Operation not supported on socket");
  1597.       break;
  1598.     case WSAEPFNOSUPPORT:
  1599.       lstrcat(pszError, "Protocol family not supported");
  1600.       break;
  1601.     case WSAEAFNOSUPPORT:
  1602.       lstrcat(pszError, "Address family not supported by protocol family");
  1603.       break;
  1604.     case WSAEADDRINUSE:
  1605.       lstrcat(pszError, "Address already in use");
  1606.       break;
  1607.     case WSAEADDRNOTAVAIL:
  1608.       lstrcat(pszError, "Can't assign requested address");
  1609.       break;
  1610.     case WSAENETDOWN:
  1611.       lstrcat(pszError, "Network is down");
  1612.       break;
  1613.     case WSAENETUNREACH:
  1614.       lstrcat(pszError, "Network is unreachable");
  1615.       break;
  1616.     case WSAENETRESET:
  1617.       lstrcat(pszError, "Network dropped connection on reset");
  1618.       break;
  1619.     case WSAECONNABORTED:
  1620.       lstrcat(pszError, "Software caused connection abort");
  1621.       break;
  1622.     case WSAECONNRESET:
  1623.       lstrcat(pszError, "Connection reset by peer");
  1624.       break;
  1625.     case WSAENOBUFS:
  1626.       lstrcat(pszError, "No buffer space available");
  1627.       break;
  1628.     case WSAEISCONN:
  1629.       lstrcat(pszError, "Socket is already connected");
  1630.       break;
  1631.     case WSAENOTCONN:
  1632.       lstrcat(pszError, "Socket is not connected");
  1633.       break;
  1634.     case WSAESHUTDOWN:
  1635.       lstrcat(pszError, "Can't send after socket shutdown");
  1636.       break;
  1637.     case WSAETOOMANYREFS:
  1638.       lstrcat(pszError, "Too many references: can't splice");
  1639.       break;
  1640.     case WSAETIMEDOUT:
  1641.       lstrcat(pszError, "Connection timed out");
  1642.       break;
  1643.     case WSAECONNREFUSED:
  1644.       lstrcat(pszError, "Connection refused");
  1645.       break;
  1646.     case WSAELOOP:
  1647.       lstrcat(pszError, "Too many levels of symbolic links");
  1648.       break;
  1649.     case WSAENAMETOOLONG:
  1650.       lstrcat(pszError, "File name too long");
  1651.       break;
  1652.     case WSAEHOSTDOWN:
  1653.       lstrcat(pszError, "Host is down");
  1654.       break;
  1655.     case WSAEHOSTUNREACH:
  1656.       lstrcat(pszError, "No route to host");
  1657.       break;
  1658.     case WSAENOTEMPTY:
  1659.       lstrcat(pszError, "Directory not empty");
  1660.       break;
  1661.     case WSAEPROCLIM:
  1662.       lstrcat(pszError, "Too many processes");
  1663.       break;
  1664.     case WSAEUSERS:
  1665.       lstrcat(pszError, "Too many users");
  1666.       break;
  1667.     case WSAEDQUOT:
  1668.       lstrcat(pszError, "Disc quota exceeded");
  1669.       break;
  1670.     case WSAESTALE:
  1671.       lstrcat(pszError, "Stale NFS file handle");
  1672.       break;
  1673.     case WSAEREMOTE:
  1674.       lstrcat(pszError, "Too many levels of remote in path");
  1675.       break;
  1676. #ifdef _WIN32
  1677.     case WSAEDISCON:
  1678.       lstrcat(pszError, "Disconnect");
  1679.       break;
  1680. #endif
  1681.     case WSASYSNOTREADY:
  1682.       lstrcat(pszError, "Network sub-system is unusable");
  1683.       break;
  1684.     case WSAVERNOTSUPPORTED:
  1685.       lstrcat(pszError, "WinSock DLL cannot support this application");
  1686.       break;
  1687.     case WSANOTINITIALISED:
  1688.       lstrcat(pszError, "WinSock not initialized");
  1689.       break;
  1690.     case WSAHOST_NOT_FOUND:
  1691.       lstrcat(pszError, "Host not found");
  1692.       break;
  1693.     case WSATRY_AGAIN:
  1694.       lstrcat(pszError, "Non-authoritative host not found");
  1695.       break;
  1696.     case WSANO_RECOVERY:
  1697.       lstrcat(pszError, "Non-recoverable error");
  1698.       break;
  1699.     case WSANO_DATA:
  1700.       lstrcat(pszError, "Valid name, no data record of requested type");
  1701.       break;
  1702.     default:
  1703.       lstrcpy(pszError, "Not a WinSock error");
  1704.       break;
  1705.   }
  1706.  
  1707.   lstrcat(pszError, "\n");
  1708.  
  1709.   int n = lstrlen(pszError);
  1710.   if (pszMessage != NULL)
  1711.     n += lstrlen(pszMessage);
  1712.   if ((pszMessage != NULL) && (n < ERROR_BUF_LEN))
  1713.     lstrcat(pszError, pszMessage);
  1714.  
  1715.   AfxMessageBox(pszError);
  1716. }
  1717.